﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using H2.DataTypes;
using H2.Streams;

namespace Mystery.SCNR
{
    public class SpawnData
    {
        public const int Offset = 792;
        public const int Size = 96;

        public const int StaticRespawnZoneOffset = 80;
        public const int StaticInitialSpawnZoneOffset = 88;

        public float DynamicSpawnHeightLower;
        public float DynamicSpawnHeightUpper;
        public float GameObjectiveResetHeight;

        public byte[] unused;

        public StaticZone[] StaticRespawnZones;
        public StaticZone[] StaticInitialSpawnZones;

        public class StaticZone
        {
            public const int Size = 48;

            public StringID Name;
            public uint RelevantTeams;
            public uint RelevantGames;
            public uint Flags;
            public float PositionX;
            public float PositionY;
            public float PositionZ;
            public float LowerHeight;
            public float UpperHeight;
            public float InnerRadius;
            public float OuterRadius;
            public float Weight;

            public enum Teams
            {
                Red = 1,
                Blue = 2,
                Yellow = 4,
                Green = 8,
                Purple = 16,
                Orange = 32,
                Brown = 64,
                Pink = 128,
                Neutral = 256,
                AllExceptNeutral = 255
            }

            public enum Games
            {
                Slayer = 1,
                Oddball = 2,
                KOTH = 4,
                CTF = 8,
                Race = 16,
                Headhunter = 32,
                Juggernaught = 64,
                Territories = 128,
            }

            public enum FlagsEnum
            {
                DisabledIfFlagHome = 1,
                DisabledIfFlagAway = 2,
                DisabledIfBombHome = 4,
                DisabledIfBombAway = 8,
            }

            public void ReadFrom(MapStream map)
            {
                Name = map.ReadStringID();
                RelevantTeams = map.ReadUInt32();
                RelevantGames = map.ReadUInt32();
                Flags = map.ReadUInt32();
                PositionX = map.ReadFloat();
                PositionY = map.ReadFloat();
                PositionZ = map.ReadFloat();
                LowerHeight = map.ReadFloat();
                UpperHeight = map.ReadFloat();
                InnerRadius = map.ReadFloat();
                OuterRadius = map.ReadFloat();
                Weight = map.ReadFloat();
            }

            public void WriteTo(BaseStream stream)
            {
                stream.Write(0);
                stream.Write(RelevantTeams);
                stream.Write(RelevantGames);
                stream.Write(Flags);
                stream.Write(PositionX);
                stream.Write(PositionY);
                stream.Write(PositionZ);
                stream.Write(LowerHeight);
                stream.Write(UpperHeight);
                stream.Write(InnerRadius);
                stream.Write(OuterRadius);
                stream.Write(Weight);
            }
        }

        public static SpawnData Read(MapStream map, int scnrOffset)
        {
            if (map.ReadReflexiveAt(scnrOffset + Offset) == 0) return null;

            SpawnData s = new SpawnData();

            s.DynamicSpawnHeightLower = map.ReadFloat();
            s.DynamicSpawnHeightUpper = map.ReadFloat();
            s.GameObjectiveResetHeight = map.ReadFloat();
            s.unused = map.ReadBytes(68);
            
            map.PushBookmark();
            s.StaticRespawnZones = new StaticZone[map.ReadReflexive()];
            for (int i = 0; i < s.StaticRespawnZones.Length; i++)
            {
                s.StaticRespawnZones[i] = new StaticZone();
                s.StaticRespawnZones[i].ReadFrom(map);
            }
            map.PopBookmark();

            map.PushBookmark();
            s.StaticInitialSpawnZones = new StaticZone[map.ReadReflexive()];
            for (int i = 0; i < s.StaticInitialSpawnZones.Length; i++)
            {
                s.StaticInitialSpawnZones[i] = new StaticZone();
                s.StaticInitialSpawnZones[i].ReadFrom(map);
            }
            map.PopBookmark();

            return s;
        }

        public static void Generate(Scenario scnr, AISquad[] aiSquads)
        {
            List<StaticZone> initialSpawnZones = new List<StaticZone>();
            List<StaticZone> respawnZones = new List<StaticZone>();
            foreach (AISquad ais in aiSquads)
            {
                if (ais.NetgameBaseTeam >= 0 && ais.StartingLocations.Length > 0)
                {
                    if (ais.NetgameBaseTeam != 8)
                    {
                        StaticZone initialZone = new StaticZone();
                        initialZone.RelevantTeams = (uint)(1 << ais.NetgameBaseTeam);
                        initialZone.RelevantGames = (uint)StaticZone.Games.CTF;
                        initialZone.Name = CreateStringID(GetZoneName(initialZone) + "initial_spawn");
                        initialZone.PositionX = ais.StartingLocations[0].PositionX;
                        initialZone.PositionY = ais.StartingLocations[0].PositionY;
                        initialZone.PositionZ = ais.StartingLocations[0].PositionZ;
                        initialZone.InnerRadius = 5.0f;
                        initialZone.OuterRadius = 10.0f;
                        initialZone.Weight = 5.0f;
                        initialSpawnZones.Add(initialZone);
                    }

                    StaticZone initialAntiZone = new StaticZone();
                    initialAntiZone.RelevantTeams = (uint)StaticZone.Teams.AllExceptNeutral;
                    initialAntiZone.RelevantGames = (uint)StaticZone.Games.Territories;
                    initialAntiZone.Name = CreateStringID(GetZoneName(initialAntiZone) + "initial_anti_spawn");
                    initialAntiZone.PositionX = ais.StartingLocations[0].PositionX;
                    initialAntiZone.PositionY = ais.StartingLocations[0].PositionY;
                    initialAntiZone.PositionZ = ais.StartingLocations[0].PositionZ;
                    initialAntiZone.InnerRadius = 5.0f;
                    initialAntiZone.OuterRadius = 10.0f;
                    initialAntiZone.Weight = -1.0f;
                    initialSpawnZones.Add(initialAntiZone);

                    if (ais.NetgameBaseTeam != 8)
                    {
                        StaticZone respawnZone = new StaticZone();
                        respawnZone.RelevantTeams = (uint)(1 << ais.NetgameBaseTeam);
                        respawnZone.RelevantGames = (uint)StaticZone.Games.CTF;
                        respawnZone.Name = CreateStringID(GetZoneName(respawnZone) + "respawn");
                        respawnZone.PositionX = ais.StartingLocations[0].PositionX;
                        respawnZone.PositionY = ais.StartingLocations[0].PositionY;
                        respawnZone.PositionZ = ais.StartingLocations[0].PositionZ;
                        respawnZone.InnerRadius = 10.0f;
                        respawnZone.OuterRadius = 15.0f;
                        respawnZone.Weight = 2.5f;
                        respawnZones.Add(respawnZone);
                    }
                }
            }

            SpawnData s = new SpawnData();
            s.StaticInitialSpawnZones = initialSpawnZones.ToArray();
            s.StaticRespawnZones = respawnZones.ToArray();
            s.GameObjectiveResetHeight = -10.0f;
            s.unused = new byte[68];
            scnr.SpawnData = s;
        }

        public static string GetZoneName(StaticZone zone)
        {
            string name = string.Empty;

            switch ((StaticZone.Teams)zone.RelevantTeams)
            {
                case StaticZone.Teams.Red:
                    name += "alpha_";
                    break;
                case StaticZone.Teams.Blue:
                    name += "bravo_";
                    break;
                case StaticZone.Teams.Yellow:
                    name += "team2_";
                    break;
                case StaticZone.Teams.Green:
                    name += "team3_";
                    break;
            }

            switch ((StaticZone.Games)zone.RelevantGames)
            {
                case StaticZone.Games.CTF:
                {
                    name += "ctf_";
                    break;
                }
                case StaticZone.Games.Territories:
                {
                    name += "terries_";
                    break;
                }
            }

            return name;
        }

        public static StringID CreateStringID(string name)
        {
            char[] data = name.ToCharArray();
            char[] stringIDData = new char[128];
            Array.Copy(data, stringIDData, Math.Min(128, data.Length));
            return new StringID(new string(stringIDData));
        }

        public void WriteTo(BaseStream stream)
        {
            stream.Write(DynamicSpawnHeightLower);
            stream.Write(DynamicSpawnHeightUpper);
            stream.Write(GameObjectiveResetHeight);
            stream.Write(unused);
            stream.Write(0);
            stream.Write(0);
            stream.Write(0);
            stream.Write(0);

            for (int i = 0; i < StaticRespawnZones.Length; i++)
                StaticRespawnZones[i].WriteTo(stream);

            for (int i = 0; i < StaticInitialSpawnZones.Length; i++)
                StaticInitialSpawnZones[i].WriteTo(stream);
        }
    }
}
